﻿using System;
using System.ComponentModel;
using UnityEngine;
using System.Reflection;
using System.Collections.Generic;
using System.Linq;

namespace ScFramework.Core
{
    /// <summary>
    /// 可同步数据的组件的基类
    /// </summary>
  //  [RequireComponent(typeof(SGameObject))]
    public abstract class SComponent : MonoBehaviour
    {



        List<ISyncValue> values;

        public UInt16 ValueCnt => (UInt16)values.Count;

        public void SyncValue(UInt16 id)
        {
            SetInstanceValue(id,GetValue(id));
        }

        public void SetValue(UInt16 id, object value)
        {
            values[id].Value = (value);
        }

        public object GetValue(UInt16 id)
        {
            return values[id].Value;
        }

        public object GetInstanceValue(UInt16 id)
        {
            Debug.Assert(values[id].IsPure == false);
            return values[id].InstanceValue;
        }
        public void SetInstanceValue(UInt16 id, object value)
        {
            //   Debug.Assert(values[id].IsPure == false);
            values[id].InstanceValue = value;
        }
        public bool IsValuePure(UInt16 id)
        {
            return values[id].IsPure;
        }

        /// <summary>
        /// 所包含数据
        /// </summary>
        protected ComponentData data;

        /// <summary>
        /// 所属实体
        /// </summary>

        protected WeakReference<Entity> entity;





        public Entity Entity
        {
            get
            {
                if (entity.TryGetTarget(out Entity tg))
                {
                    return tg;
                }
                else return null;
            }
        }
        public UInt16 EntityId { get => data.CompIdentity.left(); }
        public UInt16 Identity { get => data.CompIdentity.right(); }

        //    public UInt16 ParentIdentity { get => ParentID; }

        public ComponentData SData => data;
        public virtual void SetRigid(Rigidbody2D r)
        {
            rigid.SetTarget(r);
        }
        public virtual void ClearRigid()
        {
            rigid.SetTarget(null);
        }
        private WeakReference<Rigidbody2D> rigid = new WeakReference<Rigidbody2D>(null);
        public Rigidbody2D Rigid
        {
            get
            {
                if (rigid.TryGetTarget(out Rigidbody2D tg))
                {
                    return tg;
                }
                else
                {

                    return null;
                }


            }
        }
        public virtual void SFixedUpdate()
        {

        }
        /// <summary>
        /// 绑定数据及物体结构关系
        /// </summary>
        /// <param name="args"></param>
        public void SBindID(Entity e, ComponentData d)
        {
            entity = new WeakReference<Entity>(e);

            data = d;
        }
        public virtual void InitValueSetting() { }
        /// <summary>
        /// 进行数据更新前的初始化操作
        /// </summary>
        public virtual void SInit()
        {
            InitValueSetting();
            SetSyncValue();

        }

        /// <summary>
        /// 进行一些参数设置
        /// </summary>
        public virtual void SStart()
        {

        }

        void SetSyncValue()
        {
            values = new List<ISyncValue>();
            BindingFlags flags = (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | BindingFlags.DeclaredOnly);
            TravelSyncValue(this.GetType(), (FieldInfo v, UInt16 valueCnt) =>
             {

                 values.Add(new SyncValue(v.FieldType, (object value) => v.SetValue(this, value)));
                 typeof(SyncValue).GetMethod("__Init", flags ^ BindingFlags.DeclaredOnly).Invoke(values.Last(), new object[] { this.data, valueCnt });


             }, (PropertyInfo v, UInt16 valueCnt) =>
             {

                 Debug.Assert(v.CanRead);
                 if (v.CanWrite)
                 {
                     values.Add(new SyncValue(v.PropertyType, () => v.GetMethod.Invoke(this, null), (object value) => v.SetMethod.Invoke(this, new object[] { value })));
                     typeof(SyncValue).GetMethod("__Init", flags ^ BindingFlags.DeclaredOnly).Invoke(values.Last(), new object[] { this.data, valueCnt });
                 }
                 else
                 {
                     values.Add(new SyncValue(v.PropertyType, () => v.GetMethod.Invoke(this, null)));
                     typeof(SyncValue).GetMethod("__Init", flags ^ BindingFlags.DeclaredOnly).Invoke(values.Last(), new object[] { this.data, valueCnt });

                 }


             });
        }

        public virtual void SLateFixedUpdate()
        {

        }

        public virtual void SOnDestory()
        {
            ClearRigid();
            foreach (var x in values)
            {
                x.OnDestroy();
            }
            subEntityList = null;
            data = null;
            entity = null;
            values = null;
        }

        public virtual void SOnDisable()
        {
            DeleteAllSubEntity();
        }


        public virtual void SOnEnable(object args = null)
        {
            subEntityList = new List<EntityData>();
        }
        public static UInt16 TravelSyncValue(Type t, Action<FieldInfo, UInt16> f, Action<PropertyInfo, UInt16> p)
        {
            Debug.Assert(typeof(SComponent).IsAssignableFrom(t));
            ushort cnt = 0;
            if (t != typeof(SComponent))
            {
                cnt += TravelSyncValue(t.BaseType, f, p);

            }
            BindingFlags flags = (System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance | BindingFlags.DeclaredOnly);

            var fields = t.GetFields((System.Reflection.BindingFlags)flags);
            foreach (var v in fields)
            {
                if (v.GetCustomAttribute<PureValueAtrribute>() != null)
                {
        //            Debug.Log("field " + v);
                    if (f != null)
                        f(v, cnt);
                    cnt++;
                }

            }
            var ps = t.GetProperties((System.Reflection.BindingFlags)flags);
            foreach (var v in ps)
            {
                if (v.GetCustomAttribute<DynamicValueAtrribute>() != null)
                {
            //        Debug.Log("property " + v);
                    if (p != null)
                        p(v, cnt);
                    cnt++;
                }
            }




            return cnt;
        }

      
        protected List<EntityData> subEntityList;
        public void CreateEntity(string entityName,Action<EntityData>action=null,bool hasCollision=true)
        {
            Entity.entityAPI.CreateEntityData(new DataCreateArgs
            {
                ID = EntityUtilities.GetIdByName(entityName),
                actions = (EntityData e) =>
                {
                    if (action != null)
                    {
                        action(e);
                    }
                    subEntityList.Add(e);
                    if (hasCollision == false)
                    {
                        e.AddIgnorePair(EntityId);

                    }
                }
            });
        }
        public void DeleteEntity(ushort eid)
        {
            Debug.Assert(subEntityList.FindAll(s => s.EntityIdentity.right() == eid).Count==1) ;
            subEntityList.RemoveAll(s => s.EntityIdentity.right() == eid);
            Entity.entityAPI.DestoryEntityData(eid);

        }

        public void DeleteAllSubEntity()
        {
            var list = subEntityList.ConvertAll(s => s.EntityIdentity.right());

            foreach (var x in list)
            {
                DeleteEntity(x);
            }
            subEntityList.Clear();
        }
    }
   /* [RequireComponent(typeof(SGameObject))]
    public abstract class SComp : SComponent
    {

    }*/
}